/*
*
*   This program is free software; you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation; either version 2 of the License, or (at
*   your option) any later version.
*
*   This program is distributed in the hope that it will be useful, but
*   WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software Foundation,
*   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*   In addition, as a special exception, the author gives permission to
*   link the code of this program with the Half-Life Game Engine ("HL
*   Engine") and Modified Game Libraries ("MODs") developed by Valve,
*   L.L.C ("Valve").  You must obey the GNU General Public License in all
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve.  If you modify this file, you may extend this exception
*   to your version of the file, but you are not obligated to do so.  If
*   you do not wish to do so, delete this exception statement from your
*   version.
*
*/

#include "precompiled.h"

// range for snipers to select a hiding spot
const float sniperHideRange = 2000.0f;

// The Idle state.
// We never stay in the Idle state - it is a "home base" for the state machine that
// does various checks to determine what we should do next.
void IdleState::OnEnter(CCSBot *me)
{
	me->DestroyPath();
	me->SetEnemy(nullptr);

	// lurking death
	if (me->IsUsingKnife() && me->IsWellPastSafe() && !me->IsHurrying())
		me->Walk();

	// Since Idle assigns tasks, we assume that coming back to Idle means our task is complete
	me->SetTask(CCSBot::SEEK_AND_DESTROY);
	me->SetDisposition(CCSBot::ENGAGE_AND_INVESTIGATE);
}

// Determine what we should do next
void IdleState::OnUpdate(CCSBot *me)
{
	// all other states assume GetLastKnownArea() is valid, ensure that it is
	if (!me->GetLastKnownArea() && me->StayOnNavMesh() == false)
		return;

	// zombies never leave the Idle state
	if (cv_bot_zombie.value > 0.0f)
	{
		me->ResetStuckMonitor();
		return;
	}

	// if we are in the early "safe" time, grab a knife or grenade
	if (me->IsSafe())
	{
		// if we have a grenade, use it
		if (!me->EquipGrenade())
		{
			// high-skill bots run with the knife
			if (me->GetProfile()->GetSkill() > 0.33f)
			{
				me->EquipKnife();
			}
		}
	}

	// if round is over, hunt
	if (me->GetGameState()->IsRoundOver())
	{
		// if we are escorting hostages, try to get to the rescue zone
		if (me->GetHostageEscortCount())
		{
			const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(me->GetLastKnownArea(), PathCost(me, FASTEST_ROUTE));
			const Vector *zonePos = TheCSBots()->GetRandomPositionInZone(zone);
#ifdef REGAMEDLL_FIXES
			if (zonePos)
#endif
			{
				me->SetTask(CCSBot::RESCUE_HOSTAGES);
				me->Run();
				me->SetDisposition(CCSBot::SELF_DEFENSE);
				me->MoveTo(zonePos, FASTEST_ROUTE);
				me->PrintIfWatched("Trying to rescue hostages at the end of the round\n");
				return;
			}
		}

		me->Hunt();
		return;
	}

	const float defenseSniperCampChance = 75.0f;
	const float offenseSniperCampChance = 10.0f;

	// if we were following someone, continue following them
	if (me->IsFollowing())
	{
		me->ContinueFollowing();
		return;
	}

	// Scenario logic
	switch (TheCSBots()->GetScenario())
	{
	case CCSBotManager::SCENARIO_DEFUSE_BOMB:
	{
		// if this is a bomb game and we have the bomb, go plant it
		if (me->m_iTeam == TERRORIST)
		{
			if (me->GetGameState()->IsBombPlanted())
			{
				if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN)
				{
					// T's always know where the bomb is - go defend it
					const CCSBotManager::Zone *zone = TheCSBots()->GetZone(me->GetGameState()->GetPlantedBombsite());
#ifdef REGAMEDLL_FIXES
					if (zone)
#endif
					{
						me->SetTask(CCSBot::GUARD_TICKING_BOMB);

						Place place = TheNavAreaGrid.GetPlace(&zone->m_center);
						if (place != UNDEFINED_PLACE)
						{
							// pick a random hiding spot in this place
							const Vector *spot = FindRandomHidingSpot(me, place, me->IsSniper());
							if (spot)
							{
								me->Hide(spot);
								return;
							}
						}

						// hide nearby
						me->Hide(TheNavAreaGrid.GetNearestNavArea(&zone->m_center));
						return;
					}
				}
				else
				{
					// ask our teammates where the bomb is
					me->GetChatter()->RequestBombLocation();

					// we dont know where the bomb is - we must search the bombsites
					int zoneIndex = me->GetGameState()->GetNextBombsiteToSearch();

					// move to bombsite - if we reach it, we'll update its cleared status, causing us to select another
					const Vector *pos = TheCSBots()->GetRandomPositionInZone(TheCSBots()->GetZone(zoneIndex));
					if (pos)
					{
						me->SetTask(CCSBot::FIND_TICKING_BOMB);
						me->MoveTo(pos);
						return;
					}
				}
			}
			else if (me->IsCarryingBomb())
			{
				// if we're at a bomb site, plant the bomb
				if (me->IsAtBombsite())
				{
					// plant it
					me->SetTask(CCSBot::PLANT_BOMB);
					me->PlantBomb();

					// radio to the team
					me->GetChatter()->PlantingTheBomb(me->GetPlace());

					return;
				}
				else if (TheCSBots()->IsTimeToPlantBomb())
				{
					// move to the closest bomb site
					const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(me->GetLastKnownArea(), PathCost(me));
					if (zone)
					{
						// pick a random spot within the bomb zone
						const Vector *pos = TheCSBots()->GetRandomPositionInZone(zone);
						if (pos)
						{
							// move to bombsite
							me->SetTask(CCSBot::PLANT_BOMB);
							me->Run();
							me->MoveTo(pos);

							return;
						}
					}
				}
			}
			else
			{
				// small chance of sniper camping on offense, if we aren't carrying the bomb
				if (me->GetFriendsRemaining() && me->IsSniper() && RANDOM_FLOAT(0, 100.0f) < offenseSniperCampChance)
				{
					me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
					me->Hide(me->GetLastKnownArea(), RANDOM_FLOAT(10.0f, 30.0f), sniperHideRange);
					me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
					me->PrintIfWatched("Sniping!\n");
					return;
				}

				// if the bomb is loose (on the ground), go get it
				if (me->NoticeLooseBomb())
				{
					me->FetchBomb();
					return;
				}

				// if bomb has been planted, and we hear it, move to a hiding spot near the bomb and guard it
				if (!me->IsRogue() && me->GetGameState()->IsBombPlanted() && me->GetGameState()->GetBombPosition())
				{
					const Vector *bombPos = me->GetGameState()->GetBombPosition();

					if (bombPos)
					{
						me->SetTask(CCSBot::GUARD_TICKING_BOMB);
						me->Hide(TheNavAreaGrid.GetNavArea(bombPos));
						return;
					}
				}
			}
		}
		// CT
		else
		{
			if (me->GetGameState()->IsBombPlanted())
			{
				// if the bomb has been planted, attempt to defuse it
				const Vector *bombPos = me->GetGameState()->GetBombPosition();
				if (bombPos)
				{
					// if someone is defusing the bomb, guard them
					if (TheCSBots()->GetBombDefuser())
					{
						if (!me->IsRogue())
						{
							me->SetTask(CCSBot::GUARD_BOMB_DEFUSER);
							me->Hide(TheNavAreaGrid.GetNavArea(bombPos));
							return;
						}
					}
					else if (me->IsDoingScenario())
					{
						// move to the bomb and defuse it
						me->SetTask(CCSBot::DEFUSE_BOMB);
						me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
						me->MoveTo(bombPos);
						return;
					}
					else
					{
						// we're not allowed to defuse, guard the bomb zone
						me->SetTask(CCSBot::GUARD_BOMB_ZONE);
						me->Hide(TheNavAreaGrid.GetNavArea(bombPos));
						me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
						return;
					}
				}
				else if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN)
				{
					// we know which bombsite, but not exactly where the bomb is, go there
					const CCSBotManager::Zone *zone = TheCSBots()->GetZone(me->GetGameState()->GetPlantedBombsite());
					if (zone)
					{
						if (me->IsDoingScenario())
						{
							me->SetTask(CCSBot::DEFUSE_BOMB);
							me->MoveTo(&zone->m_center);
							me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
							return;
						}
						else
						{
							// we're not allowed to defuse, guard the bomb zone
							me->SetTask(CCSBot::GUARD_BOMB_ZONE);
							me->Hide(TheNavAreaGrid.GetNavArea(&zone->m_center));
							me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
							return;
						}
					}
				}
				else
				{
					// we dont know where the bomb is - we must search the bombsites
					// find closest un-cleared bombsite
					const CCSBotManager::Zone *zone = nullptr;
					float travelDistance = 9999999.9f;

					for (int z = 0; z < TheCSBots()->GetZoneCount(); z++)
					{
						if (TheCSBots()->GetZone(z)->m_areaCount == 0)
							continue;

						// don't check bombsites that have been cleared
						if (me->GetGameState()->IsBombsiteClear(z))
							continue;

						// just use the first overlapping nav area as a reasonable approximation
						ShortestPathCost pathCost = ShortestPathCost();
						real_t dist = NavAreaTravelDistance(me->GetLastKnownArea(), TheNavAreaGrid.GetNearestNavArea(&TheCSBots()->GetZone(z)->m_center), pathCost);

#ifdef REGAMEDLL_FIXES
						if (dist < 0.0f)
							continue;
#endif

						if (dist < travelDistance)
						{
							zone = TheCSBots()->GetZone(z);
							travelDistance = dist;
						}
					}

					if (zone)
					{
						const float farAwayRange = 2000.0f;
						if (travelDistance > farAwayRange)
						{
							zone = nullptr;
						}
					}

					// if closest bombsite is "far away", pick one at random
					if (!zone)
					{
						int zoneIndex = me->GetGameState()->GetNextBombsiteToSearch();
						zone = TheCSBots()->GetZone(zoneIndex);
					}

					// move to bombsite - if we reach it, we'll update its cleared status, causing us to select another
					if (zone)
					{
						const Vector *pos = TheCSBots()->GetRandomPositionInZone(zone);
						if (pos)
						{
							me->SetTask(CCSBot::FIND_TICKING_BOMB);
							me->MoveTo(pos);
							return;
						}
					}
				}

				assert((0, "A CT bot doesn't know what to do while the bomb is planted!\n"));
			}

			// if we have a sniper rifle, we like to camp, whether rogue or not
			if (me->IsSniper())
			{
				if (RANDOM_FLOAT(0, 100) <= defenseSniperCampChance)
				{
					CNavArea *snipingArea = nullptr;

					// if the bomb is loose, snipe near it
					if (me->GetGameState()->IsLooseBombLocationKnown())
					{
						snipingArea = TheNavAreaGrid.GetNearestNavArea(me->GetGameState()->GetBombPosition());
						me->PrintIfWatched("Sniping near loose bomb\n");
					}
					else
					{
						// snipe bomb zone(s)
						const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
						if (zone)
						{
							snipingArea = TheCSBots()->GetRandomAreaInZone(zone);
							me->PrintIfWatched("Sniping near bombsite\n");
						}
					}

					if (snipingArea)
					{
						me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
						me->Hide(snipingArea, -1.0f, sniperHideRange);
						me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
						return;
					}
				}
			}

			// rogues just hunt, unless they want to snipe
			// if the whole team has decided to rush, hunt
			// if we know the bomb is dropped, hunt for enemies and the loose bomb
			if (me->IsRogue() || TheCSBots()->IsDefenseRushing() || me->GetGameState()->IsLooseBombLocationKnown())
			{
				me->Hunt();
				return;
			}

			// the lower our morale gets, the more we want to camp the bomb zone(s)
			// only decide to camp at the start of the round, or if we haven't seen anything for a long time
			if (me->IsSafe() || me->HasNotSeenEnemyForLongTime())
			{
				float guardBombsiteChance = -34.0f * me->GetMorale();

				if (RANDOM_FLOAT(0.0f, 100.0f) < guardBombsiteChance)
				{
					float guardRange = 500.0f + 100.0f * (me->GetMorale() + 3);

					// guard bomb zone(s)
					const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
					if (zone)
					{
						CNavArea *area = TheCSBots()->GetRandomAreaInZone(zone);
						if (area)
						{
							me->PrintIfWatched("I'm guarding a bombsite\n");
							me->GetChatter()->AnnouncePlan("GoingToDefendBombsite", area->GetPlace());
							me->SetTask(CCSBot::GUARD_BOMB_ZONE);
							me->Hide(area, -1.0, guardRange);
							me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
							return;
						}
					}
				}
			}
		}
		break;
	}
	case CCSBotManager::SCENARIO_ESCORT_VIP:
	{
		if (me->m_iTeam == TERRORIST)
		{
			// if we have a sniper rifle, we like to camp, whether rogue or not
			if (me->IsSniper())
			{
				if (RANDOM_FLOAT(0, 100) <= defenseSniperCampChance)
				{
					// snipe escape zone(s)
					const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
					if (zone)
					{
						CNavArea *area = TheCSBots()->GetRandomAreaInZone(zone);
						if (area)
						{
							me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
							me->Hide(area, -1.0, sniperHideRange);
							me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
							me->PrintIfWatched("Sniping near escape zone\n");
							return;
						}
					}
				}
			}

			// rogues just hunt, unless they want to snipe
			// if the whole team has decided to rush, hunt
			if (me->IsRogue() || TheCSBots()->IsDefenseRushing())
				break;

			// the lower our morale gets, the more we want to camp the escape zone(s)
			float guardEscapeZoneChance = -34.0f * me->GetMorale();

			if (RANDOM_FLOAT(0.0f, 100.0f) < guardEscapeZoneChance)
			{
				// guard escape zone(s)
				const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
				if (zone)
				{
					CNavArea *area = TheCSBots()->GetRandomAreaInZone(zone);
					if (area)
					{
						// guard the escape zone - stay closer if our morale is low
						me->SetTask(CCSBot::GUARD_VIP_ESCAPE_ZONE);
						me->PrintIfWatched("I'm guarding an escape zone\n");

						float escapeGuardRange = 750.0f + 250.0f * (me->GetMorale() + 3);
						me->Hide(area, -1.0, escapeGuardRange);
						me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
						return;
					}
				}
			}
		}
		// CT
		else
		{
			if (me->m_bIsVIP)
			{
				// if early in round, pick a random zone, otherwise pick closest zone
				const float earlyTime = 20.0f;
				const CCSBotManager::Zone *zone = nullptr;

				if (TheCSBots()->GetElapsedRoundTime() < earlyTime)
				{
					// pick random zone
					zone = TheCSBots()->GetRandomZone();
				}
				else
				{
					// pick closest zone
					zone = TheCSBots()->GetClosestZone(me->GetLastKnownArea(), PathCost(me));
				}

				if (zone)
				{
					// pick a random spot within the escape zone
					const Vector *pos = TheCSBots()->GetRandomPositionInZone(zone);
					if (pos)
					{
						// move to escape zone
						me->SetTask(CCSBot::VIP_ESCAPE);
						me->Run();
						me->MoveTo(pos);

						// tell team to follow
						const float repeatTime = 30.0f;
						if (me->GetFriendsRemaining() && TheCSBots()->GetRadioMessageInterval(EVENT_RADIO_FOLLOW_ME, me->m_iTeam) > repeatTime)
							me->SendRadioMessage(EVENT_RADIO_FOLLOW_ME);
						return;
					}
				}
			}
			else
			{
				// small chance of sniper camping on offense, if we aren't VIP
				if (me->GetFriendsRemaining() && me->IsSniper() && RANDOM_FLOAT(0, 100.0f) < offenseSniperCampChance)
				{
					me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
					me->Hide(me->GetLastKnownArea(), RANDOM_FLOAT(10.0f, 30.0f), sniperHideRange);
					me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
					me->PrintIfWatched("Sniping!\n");
					return;
				}
			}
		}
		break;
	}
	case CCSBotManager::SCENARIO_RESCUE_HOSTAGES:
	{
		if (me->m_iTeam == TERRORIST)
		{
			bool campHostages;

			// if we are in early game, camp the hostages
			if (me->IsSafe())
			{
				campHostages = true;
			}
			else if (me->GetGameState()->HaveSomeHostagesBeenTaken() || me->GetGameState()->AreAllHostagesBeingRescued())
			{
				campHostages = false;
			}
			else
			{
				// later in the game, camp either hostages or escape zone
				const float campZoneChance = 100.0f * (TheCSBots()->GetElapsedRoundTime() - me->GetSafeTime()) / 120.0f;
				campHostages = (RANDOM_FLOAT(0, 100) > campZoneChance) ? true : false;
			}

			// if we have a sniper rifle, we like to camp, whether rogue or not
			if (me->IsSniper())
			{
				if (RANDOM_FLOAT(0, 100) <= defenseSniperCampChance)
				{
					const Vector *hostagePos = me->GetGameState()->GetRandomFreeHostagePosition();
					if (hostagePos && campHostages)
					{
						me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
						me->PrintIfWatched("Sniping near hostages\n");
						me->Hide(TheNavAreaGrid.GetNearestNavArea(hostagePos), -1.0, sniperHideRange);
						me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
						return;
					}
					else
					{
						// camp the escape zone(s)
						if (me->GuardRandomZone(sniperHideRange))
						{
							me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
							me->PrintIfWatched("Sniping near a rescue zone\n");
							me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
							return;
						}
					}
				}
			}

			// if safe time is up, and we stumble across a hostage, guard it
			if (!me->IsSafe() && !me->IsRogue())
			{
				CBaseEntity *pHostage = me->GetGameState()->GetNearestVisibleFreeHostage();
				if (pHostage)
				{
					// we see a free hostage, guard it
					CNavArea *area = TheNavAreaGrid.GetNearestNavArea(&pHostage->pev->origin);
					if (area)
					{
						me->SetTask(CCSBot::GUARD_HOSTAGES);
						me->Hide(area);
						me->PrintIfWatched("I'm guarding hostages I found\n");
						// don't chatter here - he'll tell us when he's in his hiding spot
						return;
					}
				}
			}

			// decide if we want to hunt, or guard
			const float huntChance = 70.0f + 25.0f * me->GetMorale();

			// rogues just hunt, unless they want to snipe
			// if the whole team has decided to rush, hunt
			if (me->GetFriendsRemaining())
			{
				if (me->IsRogue() || TheCSBots()->IsDefenseRushing() || RANDOM_FLOAT(0, 100) < huntChance)
				{
					me->Hunt();
					return;
				}
			}

			// decide whether to camp the hostages or the escape zones
			const Vector *hostagePos = me->GetGameState()->GetRandomFreeHostagePosition();
			if (hostagePos && campHostages)
			{
				CNavArea *area = TheNavAreaGrid.GetNearestNavArea(hostagePos);
				if (area)
				{
					// guard the hostages - stay closer to hostages if our morale is low
					me->SetTask(CCSBot::GUARD_HOSTAGES);
					me->PrintIfWatched("I'm guarding hostages\n");

					float hostageGuardRange = 750.0f + 250.0f * (me->GetMorale() + 3);
					me->Hide(area, -1.0, hostageGuardRange);
					me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);

					if (RANDOM_FLOAT(0, 100) < 50)
						me->GetChatter()->GuardingHostages(area->GetPlace(), IS_PLAN);

					return;
				}
			}

			// guard rescue zone(s)
			if (me->GuardRandomZone())
			{
				me->SetTask(CCSBot::GUARD_HOSTAGE_RESCUE_ZONE);
				me->PrintIfWatched("I'm guarding a rescue zone\n");
				me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
				me->GetChatter()->GuardingHostageEscapeZone(IS_PLAN);
				return;
			}
		}
		// CT
		else
		{
			// only decide to do something else if we aren't already rescuing hostages
			if (!me->GetHostageEscortCount())
			{
				// small chance of sniper camping on offense
				if (me->GetFriendsRemaining() && me->IsSniper() && RANDOM_FLOAT(0, 100.0f) < offenseSniperCampChance)
				{
					me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
					me->Hide(me->GetLastKnownArea(), RANDOM_FLOAT(10.0f, 30.0f), sniperHideRange);
					me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
					me->PrintIfWatched("Sniping!\n");
					return;
				}

				if (me->GetFriendsRemaining() && !me->GetHostageEscortCount())
				{
					// rogues just hunt, unless all friends are dead
					// if we have friends left, we might go hunting instead of hostage rescuing
					const float huntChance = 33.3f;
					if (me->IsRogue() || RANDOM_FLOAT(0.0f, 100.0f) < huntChance)
					{
						me->Hunt();
						return;
					}
				}
			}

			// look for free hostages - CT's have radar so they know where hostages are at all times
			CHostage *pHostage = me->GetGameState()->GetNearestFreeHostage();

			// if we are not allowed to do the scenario, guard the hostages to clear the area for the human(s)
			if (!me->IsDoingScenario())
			{
				if (pHostage)
				{
					CNavArea *area = TheNavAreaGrid.GetNearestNavArea(&pHostage->pev->origin);
					if (area)
					{
						me->SetTask(CCSBot::GUARD_HOSTAGES);
						me->Hide(area);
						me->PrintIfWatched("I'm securing the hostages for a human to rescue\n");
						return;
					}
				}

				me->Hunt();
				return;
			}

			bool fetchHostages = false;
			bool rescueHostages = false;
			const CCSBotManager::Zone *zone = nullptr;
			me->SetGoalEntity(nullptr);

			// if we are escorting hostages, determine where to take them
			if (me->GetHostageEscortCount())
				zone = TheCSBots()->GetClosestZone(me->GetLastKnownArea(), PathCost(me, FASTEST_ROUTE));

			// if we are escorting hostages and there are more hostages to rescue,
			// determine whether it's faster to rescue the ones we have, or go get the remaining ones
			if (pHostage)
			{
				if (zone)
				{
					PathCost pathCost(me, FASTEST_ROUTE);
					float toZone = NavAreaTravelDistance(me->GetLastKnownArea(), zone->m_area[0], pathCost);
					float toHostage = NavAreaTravelDistance(me->GetLastKnownArea(), TheNavAreaGrid.GetNearestNavArea(&pHostage->pev->origin), pathCost);

					if (toHostage < 0.0f)
					{
						rescueHostages = true;
					}
					else
					{
						if (toZone < toHostage)
							rescueHostages = true;
						else
							fetchHostages = true;
					}
				}
				else
				{
					fetchHostages = true;
				}
			}
			else if (zone)
			{
				rescueHostages = true;
			}

			if (fetchHostages)
			{
				// go get hostages
				me->SetTask(CCSBot::COLLECT_HOSTAGES);
				me->Run();
				me->SetGoalEntity(pHostage);
				me->ResetWaitForHostagePatience();

				// if we already have some hostages, move to the others by the quickest route
				RouteType route = (me->GetHostageEscortCount()) ? FASTEST_ROUTE : SAFEST_ROUTE;
				me->MoveTo(&pHostage->pev->origin, route);
				me->PrintIfWatched("I'm collecting hostages\n");
				return;
			}

			if (rescueHostages)
			{
				me->SetTask(CCSBot::RESCUE_HOSTAGES);
				me->Run();
				me->SetDisposition(CCSBot::SELF_DEFENSE);
				me->MoveTo(TheCSBots()->GetRandomPositionInZone(zone), FASTEST_ROUTE);
				me->PrintIfWatched("I'm rescuing hostages\n");
				me->GetChatter()->EscortingHostages();
				return;
			}
		}
		break;
	}
	// deathmatch
	default:
	{
		// sniping check
		if (me->GetFriendsRemaining() && me->IsSniper() && RANDOM_FLOAT(0, 100.0f) < offenseSniperCampChance)
		{
			me->SetTask(CCSBot::MOVE_TO_SNIPER_SPOT);
			me->Hide(me->GetLastKnownArea(), RANDOM_FLOAT(10.0f, 30.0f), sniperHideRange);
			me->SetDisposition(CCSBot::OPPORTUNITY_FIRE);
			me->PrintIfWatched("Sniping!\n");
			return;
		}
		break;
	}
	}

	// if we have nothing special to do, go hunting for enemies
	me->Hunt();
}
